package com.mystnihon.freeremote.loader.remote;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.http.Header;
import org.apache.http.client.CookieStore;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;

import com.mystnihon.freeremote.config.Urld;
import com.mystnihon.freeremote.util.NetworkConnection;
import com.mystnihon.freeremote.util.NetworkConnectionResult;
import com.mystnihon.loader.AbstractAsyncLoader.LoadingListener;

public class Remote {
	private static final String PARAM_KEY = "key";
	private static final String PARAM_CODE = "code";
	private static final String TAG = Remote.class.getSimpleName();
	private LinkedBlockingQueue<ExecutionParams> mQueue;
	private Context mContext;
	private boolean mExecutionLock = false;
	private BasicHttpContext mHttpContext;
	private String mRemoteCode = "";// 80749403
	// private String login = "freebox";
	// private String password = "kowlown971";
	private String login = "0223207449";
	private String password = "jaepaequ";

	private class ExecutionParams {

		// public ExecutionParams(RemoteActions code) {
		// this(-1, code, (Object[]) null);
		// }

		public ExecutionParams(int requestId, RemoteActions code, Object... params) {
			mActionCode = code;
			mParameters = params;
			// mRequestId = requestId;
		}

		public RemoteActions mActionCode;
		public Object[] mParameters;
		// public int mRequestId;
	}

	/**
	 * Constructor for Remote.
	 * 
	 * @param context
	 *            An activity or Application. Use preferably
	 *            {@link Context#getApplicationContext()} or
	 *            {@link Activity#getApplication()} to pass the context. Try
	 *            using the context-application instead of a context-activity.
	 */
	public Remote(Context context) {
		mContext = context;
		mQueue = new LinkedBlockingQueue<ExecutionParams>();

		mHttpContext = new BasicHttpContext();
		CookieStore cookieStore = new BasicCookieStore();
		mHttpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

		checkInternetPermission();
		checkNetworkPermission();
	}

	public void setRemoteCode(String remoteCode) {
		this.mRemoteCode = remoteCode;
	}

	public boolean hasRemoteCode() {
		return mRemoteCode != null && mRemoteCode.length() > 0;
	}

	public void increaseVolume(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionIncVolume));
		executeAction();
	}

	public void decreaseVolume(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionDecVolume));
		executeAction();
	}

	public void nextChannel(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionNextChannel));
		executeAction();
	}

	public void prevChannel(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionPrevChannel));
		executeAction();
	}

	public void keyPower(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionPower));
		executeAction();
	}

	public void keyLeft(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionLeft));
		executeAction();
	}

	public void keyUp(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionUp));
		executeAction();
	}

	public void keyRight(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionRight));
		executeAction();
	}

	public void keyDown(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionDown));
		executeAction();
	}

	public void keyBack(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionBack));
		executeAction();
	}

	public void keySearch(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ACtionSearch));
		executeAction();
	}

	public void keyMenu(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionMenu));
		executeAction();
	}

	public void keyInfo(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionInfo));
		executeAction();
	}

	public void keyNumber(int number) {
		System.out.println("[Remote] send : " + number);
		keyNumber(-1, number);
	}

	public void keyNumber(int requestId, int number) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionNumberPressed, number));
		executeAction();
	}

	public void keyOk() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionOk));
		executeAction();
	}

	public void keyHome() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionHome));
		executeAction();
	}

	public void keyRwd() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionRwd));
		executeAction();
	}

	public void keyPlayPause() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionPlayPause));
		executeAction();
	}

	public void keyFwd() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionFwd));
		executeAction();
	}

	public void authent(int requestId) {
		mQueue.add(new ExecutionParams(requestId, RemoteActions.ActionAuthent));
		executeAction();
	}

	public void getChannels() {
		mQueue.add(new ExecutionParams(-1, RemoteActions.ActionGetChannels));
		executeAction();
	}

	/**
	 * Toggle the lock, signaling that the previous action has been finished if
	 * there was any so that we can process the next EPG related action.
	 */
	private void executeNextAction() {
		mExecutionLock = false;
		executeAction();
	}

	/**
	 * Process the next action in the stack. If there is already an action being
	 * executed, do nothing.
	 */
	private void executeAction() {
		if (!mExecutionLock) {
			if (mQueue != null && !mQueue.isEmpty()) {
				mExecutionLock = true;
				ExecutionParams param = mQueue.poll();
				switch (param.mActionCode) {
					case ActionIncVolume:
						executeIncreaseVolume(param);
						break;
					case ActionDecVolume:
						executeDecreaseVolume(param);
						break;
					case ActionNextChannel:
						executeNextChannel(param);
						break;
					case ActionPrevChannel:
						executePrevChannel(param);
						break;
					case ActionPower:
						executePower(param);
						break;
					case ActionDown:
						executeDown(param);
						break;
					case ActionLeft:
						executeLeft(param);
						break;
					case ActionRight:
						executeRight(param);
						break;
					case ActionUp:
						executeUp(param);
						break;
					case ActionNumberPressed:
						executeNumber(param);
						break;
					case ActionBack:
						executeBack(param);
						break;
					case ACtionSearch:
						executeSearch(param);
						break;
					case ActionMenu:
						executeMenu(param);
						break;
					case ActionInfo:
						executeInfo(param);
						break;
					case ActionAuthent:
						executeGetCookie(param);
						break;
					case ActionGetChannels:
						executeGetChannels(param);
						break;
					case ActionOk:
						executeOk(param);
						break;
					case ActionHome:
						executeHome(param);
						break;
					case ActionFwd:
						executeFwd(param);
						break;
					case ActionPlayPause:
						executePlayPause(param);
						break;
					case ActionRwd:
						executeRwd(param);
						break;
				}
			}
		}

	}

	private void executeIncreaseVolume(ExecutionParams param) {

		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "vol_inc"));
		runSimpleRemoteCommand(params);
	}

	private void executeDecreaseVolume(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "vol_dec"));
		runSimpleRemoteCommand(params);
	}

	private void executeNextChannel(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "prgm_inc"));
		runSimpleRemoteCommand(params);
	}

	private void executePrevChannel(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "prgm_dec"));
		runSimpleRemoteCommand(params);
	}

	private void executeNumber(ExecutionParams param) {
		if (param.mParameters != null && param.mParameters.length > 0) {
			if (param.mParameters[0] instanceof Integer) {

				Integer kyCode = (Integer) param.mParameters[0];
				List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
				params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
				params.add(new BasicNameValuePair(PARAM_KEY, kyCode.toString()));
				runSimpleRemoteCommand(params);
			}

		}

	}

	private void executeUp(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "up"));
		runSimpleRemoteCommand(params);

	}

	private void executeRight(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "right"));
		runSimpleRemoteCommand(params);

	}

	private void executeLeft(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "left"));
		runSimpleRemoteCommand(params);

	}

	private void executeDown(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "down"));
		runSimpleRemoteCommand(params);

	}

	private void executePower(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "power"));
		runSimpleRemoteCommand(params);

	}

	private void executeOk(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "ok"));
		runSimpleRemoteCommand(params);

	}

	private void executeBack(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "red"));
		runSimpleRemoteCommand(params);

	}

	private void executeSearch(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "blue"));
		runSimpleRemoteCommand(params);

	}

	private void executeMenu(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "green"));
		runSimpleRemoteCommand(params);

	}

	private void executeInfo(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "yellow"));
		runSimpleRemoteCommand(params);

	}

	private void executeHome(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "home"));
		runSimpleRemoteCommand(params);

	}

	private void executeRwd(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "bwd"));
		runSimpleRemoteCommand(params);

	}

	private void executePlayPause(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "play"));
		runSimpleRemoteCommand(params);

	}

	private void executeFwd(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair(PARAM_CODE, mRemoteCode));
		params.add(new BasicNameValuePair(PARAM_KEY, "fwd"));
		runSimpleRemoteCommand(params);

	}

	private void executeGetChannels(ExecutionParams param) {
		ServerLoader loader = new ServerLoader(Urld.CHANNELS);
		loader.setHttpContext(mHttpContext);
		loader.loadAsync(mContext, new LoadingListener<NetworkConnectionResult>() {

			@Override
			public void onLoadFinished(NetworkConnectionResult data) {
				executeNextAction();

			}

			@Override
			public void onLoadError(NetworkConnectionResult data, String errorMessage) {
				executeNextAction();

			}
		});
	}

	private void executeGetCookie(ExecutionParams param) {
		List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
		params.add(new BasicNameValuePair("login", login));
		params.add(new BasicNameValuePair("passwd", password));
		ServerLoader loader = new ServerLoader(Urld.LOGIN, params, null, NetworkConnection.HttpMethod.Post);
		loader.setHttpContext(mHttpContext);
		loader.loadAsync(mContext, new LoadingListener<NetworkConnectionResult>() {

			@Override
			public void onLoadFinished(NetworkConnectionResult data) {
				Header[] deaders = data.mResultHeader;
				for (Header header : deaders) {
					Log.i(TAG, String.format("Header: [Name : %s] | [Value : %s]", header.getName(), header.getValue()));
				}
				Log.i(TAG, String.format("Response %s", data.mResult));
				executeNextAction();

			}

			@Override
			public void onLoadError(NetworkConnectionResult data, String errorMessage) {
				executeNextAction();

			}
		});
	}

	private void runSimpleRemoteCommand(List<BasicNameValuePair> params) {
		ServerLoader loader = new ServerLoader(Urld.REMOTE, params);
		loader.loadAsync(mContext, new LoadingListener<NetworkConnectionResult>() {

			@Override
			public void onLoadFinished(NetworkConnectionResult data) {
				executeNextAction();

			}

			@Override
			public void onLoadError(NetworkConnectionResult data, String errorMessage) {
				executeNextAction();

			}
		});
	}

	private boolean checkInternetPermission() {

		String permission = "android.permission.INTERNET";
		int res = mContext.checkCallingOrSelfPermission(permission);

		boolean permgranted = res == PackageManager.PERMISSION_GRANTED;
		if (!permgranted) { // Obligatory warning to prevent disabling. I want
							// to throw this warning we the permission is not
							// set.
			Log.e(TAG, "add <android.permission.INTERNET> to your manifest");
		}
		return permgranted;
	}

	private boolean checkNetworkPermission() {

		String permission = "android.permission.ACCESS_NETWORK_STATE";
		int res = mContext.checkCallingOrSelfPermission(permission);
		boolean permgranted = res == PackageManager.PERMISSION_GRANTED;
		if (!permgranted) { // Obligatory warning to prevent disabling. I want
							// to throw this warning we the permission is not
							// set.
			Log.e(TAG, "add <android.permission.ACCESS_NETWORK_STATE> to your manifest");
		}
		return permgranted;
	}
}
